<?php
/**
 * GioCMS
 * 
 * LICENSE
 *
 * This source file is subject to the GNU GENERAL PUBLIC LICENSE Version 2 
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-2.0.txt
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@ninhgio.com so we can send you a copy immediately.
 * 
 * @copyright	Copyright (c) 2010-2011 GioCMS (http://cms.ninhgio.com)
 * @license		http://www.gnu.org/licenses/gpl-2.0.txt GNU GENERAL PUBLIC LICENSE Version 2
 * @author		NinhGio - ninhgio@gmail.com
 * @since		1.0
 */

class Gio_Core_Csrf
{
	protected $_salt = 'salt';
	
	protected $_name = 'token';
	
	protected $_timeout = 300;
	
	protected $_session = null;
	
	protected $_csrfEnable = false;
	
	/**
	 * CSRF request method to attack site. It can take value of POST or GET
	 * @var string
	 */
	protected $_csrfRequestMethod = 'POST';
	
	/**
	 * Defines where to get the taken value from. It can take value of POST or GET
	 * @var string
	 */
	protected $_csrfRetriveMethod = 'POST';
	
	protected $_token;
	
	public function __construct($routeName)
	{
		if (isset($config['salt'])) {
			$this->_salt = $config['salt']; 
		}
		if (isset($config['name'])) {
			$this->_name = $config['name'];
		}
		if (isset($config['timeout'])) {
			$this->_timeout = $config['timeout'];
		}
		$routes = Gio_Core_Route::getInstance()->getRoutes();
		
		if ($routeName && isset($routes[$routeName])) {
			$defaults = $routes[$routeName];
			if (isset($defaults['csrf']) && 'true' == (string)$defaults['csrf']['enable']) {
				$this->_csrfEnable = true;
				$this->_csrfRequestMethod = strtoupper($defaults['csrf']['request']);
				$this->_csrfRetriveMethod = strtoupper($defaults['csrf']['retrive']);	
			}
		}
	}
	
	public function authentication()
	{
		if ($this->_csrfEnable) {
			$session = $this->_getSession();
			$json = new Services_JSON();
			$data = $json->decode($session['data']);
			$user = array();
			if ($data) {
				while(list($key, $value) = each($data)) {
					$user[$key] = $value;
				}
			}
	        
			$request = Gio_Core_Request::getInstance();
			$isValid = null;
			
			if (($request->isPost() && $this->_csrfRequestMethod == 'POST')
				|| ($this->_csrfRequestMethod == 'GET')) 
			{
				switch ($this->_csrfRetriveMethod) {
					case 'POST':
						$token = $request->getPost($this->_name);
						break;
					case 'GET':
						$token = $request->getParam($this->_name);
						break;
				}
				$isValid = $this->isValidToken($token);
			}
			
			if ($isValid === false) {
				//throw new RuntimeException('Token does not match');
				Modules_Core_Services_Exception::error('DATA_NOT_FOUND');
				return;
			}
		}
		return true;
	}
	
	public function appendElement()
	{
		if ($this->_csrfEnable) {
			$element = sprintf('<input type="hidden" name="%s" value="%s" />',
				$this->_name,
				$this->getToken()
			);
			$view = Gio_Core_View::getInstance();
			$view->tokenElement = $element;
		}
	}
	
	public function getTokenName() 
	{
		return $this->_name;	
	}
		
	public function isValidToken($token = null)
	{
		if (null == $token || '' == $token) {
			return false;
		}
		return ($token == $this->getToken());
	}
	
	public function getToken()
	{
		$session = $this->_getSession();
		$json = new Services_JSON();
		$data = $json->decode($session['data']);
		$user = array();
		if ($data) {
			while(list($key, $value) = each($data)) {
				$user[$key] = $value;
			}
		}
		if (!isset($user['token']) || null == $user['token']) {
			/**
			 * We need to regenerate token
			 */
	        $user['token'] = $this->_generateToken();
	        $session['data'] = $json->encodeUnsafe($user);
	        Gio_Core_Session::update($session['data'], $session['session_id']);
		}
		return $user['token'];
	}	
	
	private function _getSession()
	{
		if (null == $this->_session) {
			$this->_session = Gio_Core_Session::getSessionById();
		}
		return $this->_session;
	}
	
	private function _getSessionName() 
	{
		return __CLASS__ . $this->_salt . $this->_name;
	}
	
	/**
	 * @return string
	 */
	private function _generateToken()
	{
		$token = md5(
            mt_rand(1, 1000000)
            .  $this->_salt
            .  $this->_name
            .  mt_rand(1, 1000000)
        );
        return $token;
	}
}
